package com.example.demo.aop;


import com.example.demo.bean.LogData;
import com.example.demo.component.LogHelper;
import com.example.demo.util.JsonUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

@Aspect
@Component
@Order(-5)
public class WebLogAspect {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Value("${spring.application.name}")
    private String applicationName;

    @Autowired
    private LogHelper logHelper;

    private static ThreadLocal<Long> startTime = new ThreadLocal<>();


    @Pointcut("execution(public * com.example.demo.controller..*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        startTime.set(Long.valueOf(System.currentTimeMillis()));
    }


    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) {

        Class cls = joinPoint.getSignature().getDeclaringType();
        RequestMapping requestMapping = (RequestMapping) cls.getAnnotation(RequestMapping.class);
        String classUrl = null;
        if (requestMapping != null) {
            String[] cPaths = requestMapping.value();
            if (cPaths != null && cPaths.length > 0) {
                classUrl = cPaths[0];
            }
        }
        Signature sig = joinPoint.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = joinPoint.getTarget();
        Method method = null;
        try {
            method = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        RequestMapping methodRequestMapping = method.<RequestMapping>getAnnotation(RequestMapping.class);
        String apiPath = null;
        if (methodRequestMapping != null) {
            String[] apis = methodRequestMapping.value();
            if (apis != null && apis.length > 0) {
                apiPath = apis[0];
            }
        }
        String body = "";
        Object[] inputArgs = joinPoint.getArgs();
        List<Object> inputArgsList = new ArrayList();
        if (inputArgs != null && inputArgs.length > 0) {
            for (Object obj : inputArgs) {
                if (!(obj instanceof org.springframework.validation.BindingResult)) {
                    inputArgsList.add(obj);
                }
            }
            try {
                body = JsonUtil.obj2String(inputArgsList);
            } catch (Exception ex) {
                if (log.isErrorEnabled()) {
                    log.error("--> Request input param transform to json exception, msg:{}", ex.getMessage());
                }
            }
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            if (request == null) {
                return;
            }
            LogData loggerDto = new LogData();
            loggerDto.setCreateTime(Long.valueOf(System.currentTimeMillis()));
            try {
                String requestMethord = request.getRequestURI();
                if (requestMethord == null) ;
                loggerDto.setServiceName(this.applicationName);
                if (StringUtils.isNoneBlank(classUrl)) {
                    loggerDto.setApiUrl(String.format("%s%s", new Object[]{classUrl, apiPath}));
                } else {
                    loggerDto.setApiUrl(apiPath);
                }
                loggerDto.setTransactionId(UUID.randomUUID().toString());
                loggerDto.setUrl((request.getRequestURL() == null) ? "" : request.getRequestURL().toString());
                loggerDto.setHttpMethod(request.getMethod());
                loggerDto.setIp(request.getHeader("host"));
                Map<String, String> paramsMap = new HashMap<>();
                Map<String, String> argsMap = new HashMap<>();
                Enumeration<String> enu = request.getParameterNames();
                while (enu.hasMoreElements()) {
                    String paraName = enu.nextElement();
                    paramsMap.put(paraName, request.getParameter(paraName));
                }
                String params = JsonUtil.obj2String(paramsMap);
                argsMap.put("params", params);
                if (StringUtils.isNotBlank(body)) {
                    argsMap.put("body", body);
                }
                String args = JsonUtil.obj2String(argsMap);
                loggerDto.setArgs(args);
            } catch (Exception e) {
                if (log.isErrorEnabled()) {
                    log.error("--> Commit log info exception.", e);
                }
            }
            if (ret != null) {
                loggerDto.setResponse(JsonUtil.obj2String(ret));
            }
            long costTime = System.currentTimeMillis() - ((Long) startTime.get()).longValue();
            loggerDto.setSpendTime(Long.valueOf(costTime));
            startTime.remove();

            logHelper.sendMsg(JsonUtil.obj2String(loggerDto));
        }


    }
}
